package com.easy.mongodb.core.conditions.interfaces;

import com.easy.mongodb.common.utils.CollectionUtils;
import com.easy.mongodb.common.utils.ExceptionUtils;
import com.easy.mongodb.core.biz.DistanceUnit;
import com.easy.mongodb.core.biz.GeoPoint;
import com.easy.mongodb.core.biz.ShapeRelation;
import com.easy.mongodb.core.toolkit.FieldUtils;
import com.mongodb.client.model.geojson.Geometry;

import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;


/**
 * ProjectName: easy-mongodb
 * Description: 地理位置
 * Author: vapeshop
 * Date: 2022/6/20 11:08:58
 * UpdateUser: vapeshop
 * UpdateDate: 2022/6/20 11:08:58
 * UpdateRemark: The modified content
 * Version: 1.0
 * <p>
 * Copyright © 2022 vapeshop Technologies Inc. All Rights Reserved
 **/
public interface Geo<Children, R> extends Serializable {
    /**
     * 矩形内范围查询
     *
     * @param condition   条件
     * @param column      列
     * @param topLeft     左上点坐标 GeoPoint
     * @param bottomRight 右下点坐标 GeoPoint
     * @return 泛型
     */
    Children geoBoundingBox(boolean condition, String column, GeoPoint topLeft, GeoPoint bottomRight);

    /**
     * 距离范围查询 以给定圆心和半径范围查询 距离类型为双精度
     *
     * @param condition       条件
     * @param column          列
     * @param distance        距离
     * @param distanceUnit    距离单位
     * @param centralGeoPoint 中心点 GeoPoint
     * @return 泛型
     */
    Children geoDistance(boolean condition, String column, Double distance, DistanceUnit distanceUnit, GeoPoint centralGeoPoint);


    /**
     * 距离范围查询 以给定圆心和半径范围查询 距离类型为字符串
     *
     * @param condition       条件
     * @param column          列
     * @param distance        距离
     * @param centralGeoPoint 中心点 GeoPoint
     * @return 泛型
     */
    Children geoDistance(boolean condition, String column, String distance, GeoPoint centralGeoPoint);


    /**
     * 不规则多边形范围查询
     *
     * @param condition 条件
     * @param column    列
     * @param geoPoints 多边形顶点列表 GeoPoint
     * @return 泛型
     */
    Children geoPolygon(boolean condition, String column, List<GeoPoint> geoPoints);


    /**
     * 图形GeoShape查询 用户指定图形
     * 点(Point) 线(LineString) 多边形(Polygon) 多个点(MultiPoint) 多条线(MultiLineString) 多个多边形(MultiPolygon)
     * 几何集合(GeometryCollection） 多个坐标对(Legacy Coordinate Pairs)
     *
     * @param condition     条件
     * @param column        列
     * @param geometry      图形
     * @param shapeRelation 图形关系(可参考ShapeRelation枚举)
     * @return 泛型
     */
    Children geoShape(boolean condition, String column, Geometry geometry, ShapeRelation shapeRelation);


    default Children geoBoundingBox(R column, GeoPoint topLeft, GeoPoint bottomRight) {
        return geoBoundingBox(true, column, topLeft, bottomRight);
    }


    default Children geoBoundingBox(R column, String topLeft, String bottomRight) {
        GeoPoint topLeftGeoPoint = new GeoPoint(topLeft);
        GeoPoint bottomRightGeoPoint = new GeoPoint(bottomRight);
        return geoBoundingBox(true, column, topLeftGeoPoint, bottomRightGeoPoint);
    }

    default Children geoBoundingBox(boolean condition, R column, String topLeft, String bottomRight) {
        GeoPoint topLeftGeoPoint = new GeoPoint(topLeft);
        GeoPoint bottomRightGeoPoint = new GeoPoint(bottomRight);
        return geoBoundingBox(condition, column, topLeftGeoPoint, bottomRightGeoPoint);
    }

    default Children geoBoundingBox(String column, GeoPoint topLeft, GeoPoint bottomRight) {
        return geoBoundingBox(true, column, topLeft, bottomRight);
    }


    default Children geoBoundingBox(String column, String topLeft, String bottomRight) {
        GeoPoint topLeftGeoPoint = new GeoPoint(topLeft);
        GeoPoint bottomRightGeoPoint = new GeoPoint(bottomRight);
        return geoBoundingBox(true, column, topLeftGeoPoint, bottomRightGeoPoint);
    }

    default Children geoBoundingBox(boolean condition, String column, String topLeft, String bottomRight) {
        GeoPoint topLeftGeoPoint = new GeoPoint(topLeft);
        GeoPoint bottomRightGeoPoint = new GeoPoint(bottomRight);
        return geoBoundingBox(condition, column, topLeftGeoPoint, bottomRightGeoPoint);
    }


    default Children geoBoundingBox(boolean condition, R column, GeoPoint topLeft, GeoPoint bottomRight) {
        return geoBoundingBox(condition, FieldUtils.getFieldName(column), topLeft, bottomRight);
    }

    default Children geoDistance(R column, Double distance, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, DistanceUnit.KILOMETERS, centralGeoPoint);
    }

    default Children geoDistance(R column, Double distance, DistanceUnit distanceUnit, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, distanceUnit, centralGeoPoint);
    }


    default Children geoDistance(R column, Double distance, DistanceUnit distanceUnit, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(true, column, distance, distanceUnit, geoPoint);
    }

    default Children geoDistance(boolean condition, R column, Double distance, DistanceUnit distanceUnit, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(condition, column, distance, distanceUnit, geoPoint);
    }


    default Children geoDistance(String column, Double distance, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, DistanceUnit.KILOMETERS, centralGeoPoint);
    }

    default Children geoDistance(String column, Double distance, DistanceUnit distanceUnit, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, distanceUnit, centralGeoPoint);
    }


    default Children geoDistance(String column, Double distance, DistanceUnit distanceUnit, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(true, column, distance, distanceUnit, geoPoint);
    }

    default Children geoDistance(boolean condition, String column, Double distance, DistanceUnit distanceUnit, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(condition, column, distance, distanceUnit, geoPoint);
    }


    default Children geoDistance(boolean condition, R column, Double distance, DistanceUnit distanceUnit, GeoPoint centralGeoPoint) {
        return geoDistance(condition, FieldUtils.getFieldName(column), distance, distanceUnit, centralGeoPoint);
    }


    default Children geoDistance(R column, String distance, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, centralGeoPoint);
    }


    default Children geoDistance(R column, String distance, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(true, column, distance, geoPoint);
    }

    default Children geoDistance(boolean condition, R column, String distance, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(condition, column, distance, geoPoint);
    }


    default Children geoDistance(String column, String distance, GeoPoint centralGeoPoint) {
        return geoDistance(true, column, distance, centralGeoPoint);
    }


    default Children geoDistance(String column, String distance, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(true, column, distance, geoPoint);
    }

    default Children geoDistance(boolean condition, String column, String distance, String centralGeoPoint) {
        GeoPoint geoPoint = new GeoPoint(centralGeoPoint);
        return geoDistance(condition, column, distance, geoPoint);
    }


    default Children geoDistance(boolean condition, R column, String distance, GeoPoint centralGeoPoint) {
        return geoDistance(condition, FieldUtils.getFieldName(column), distance, centralGeoPoint);
    }


    default Children geoPolygon(R column, List<GeoPoint> geoPoints) {
        return geoPolygon(true, column, geoPoints);
    }

    default Children geoPolygonStr(R column, List<String> strPoints) {
        if (CollectionUtils.isEmpty(strPoints)) {
            throw ExceptionUtils.build("polygon point list must not be empty");
        }
        List<GeoPoint> geoPoints = strPoints.stream().map(GeoPoint::new).collect(Collectors.toList());
        return geoPolygon(true, column, geoPoints);
    }

    default Children geoPolygonStr(boolean condition, R column, List<String> strPoints) {
        if (CollectionUtils.isEmpty(strPoints)) {
            throw ExceptionUtils.build("polygon point list must not be empty");
        }
        List<GeoPoint> geoPoints = strPoints.stream().map(GeoPoint::new).collect(Collectors.toList());
        return geoPolygon(condition, column, geoPoints);
    }


    default Children geoPolygon(String column, List<GeoPoint> geoPoints) {
        return geoPolygon(true, column, geoPoints);
    }


    default Children geoPolygonStr(String column, List<String> strPoints) {
        if (CollectionUtils.isEmpty(strPoints)) {
            throw ExceptionUtils.build("polygon point list must not be empty");
        }
        List<GeoPoint> geoPoints = strPoints.stream().map(GeoPoint::new).collect(Collectors.toList());
        return geoPolygon(true, column, geoPoints);
    }

    default Children geoPolygonStr(boolean condition, String column, List<String> strPoints) {
        if (CollectionUtils.isEmpty(strPoints)) {
            throw ExceptionUtils.build("polygon point list must not be empty");
        }
        List<GeoPoint> geoPoints = strPoints.stream().map(GeoPoint::new).collect(Collectors.toList());
        return geoPolygon(condition, column, geoPoints);
    }


    default Children geoPolygon(boolean condition, R column, List<GeoPoint> geoPoints) {
        return geoPolygon(condition, FieldUtils.getFieldName(column), geoPoints);
    }

    default Children geoShape(R column, Geometry geometry) {
        return geoShape(true, column, geometry, ShapeRelation.INCLUSION);
    }

    default Children geoShape(R column, Geometry geometry, ShapeRelation shapeRelation) {
        return geoShape(true, column, geometry, shapeRelation);
    }

    default Children geoShape(boolean condition, R column, Geometry geometry, ShapeRelation shapeRelation) {
        return geoShape(condition, column, geometry, shapeRelation);
    }


    default Children geoShape(String column, Geometry geometry) {
        return geoShape(true, column, geometry, ShapeRelation.INCLUSION);
    }

    default Children geoShape(String column, Geometry geometry, ShapeRelation shapeRelation) {
        return geoShape(true, column, geometry, shapeRelation);
    }
}

